package com.avoid.easymqtt.remoting.common;

import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.util.Properties;

import static com.avoid.easymqtt.remoting.netty.TlsSystemConfig.*;

/**
 * 描述
 *
 * @author avoid@fiture.com
 * @date 2022/5/5
 * @motto Life is so short,do something to make yourself happy,such as coding
 */
public class TlsHelper {

    private static final Logger log = LoggerFactory.getLogger(RemotingHelper.EASYMQTT_REMOTING);

    public static SslContext buildSslContext() throws IOException, CertificateException {
        File configFile = new File(tlsConfigFile);
        // 从文件中提取Tls配置
        extractTlsConfigFromFile(configFile);
        // 日志打印最终使用的Tls配置
        logTheFinalUsedTlsConfig();

        SslProvider provider;
        if (OpenSsl.isAvailable()) {
            provider = SslProvider.OPENSSL;
            log.info("使用OPENSSL最为最终SSL提供者");
        } else {
            provider = SslProvider.JDK;
            log.info("使用JDK最为最终SSL提供者");
        }

        SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(
            !isNullOrEmpty(tlsServerCertPath) ? new FileInputStream(tlsServerCertPath) : null,
            !isNullOrEmpty(tlsServerKeyPath) ? new FileInputStream(tlsServerKeyPath) : null,
            !isNullOrEmpty(tlsServerKeyPassword) ? tlsServerKeyPassword : null)
            .sslProvider(provider);

        if (!tlsServerAuthClient) {
            sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
        } else {
            if (!isNullOrEmpty(tlsServerTrustCertPath)) {
                sslContextBuilder.trustManager(new File(tlsServerTrustCertPath));
            }
        }

        sslContextBuilder.clientAuth(parseClientAuthMode(tlsServerNeedClientAuth));
        return sslContextBuilder.build();

    }

    private static void extractTlsConfigFromFile(final File configFile) {
        if (!(configFile.exists() && configFile.isFile() && configFile.canRead())) {
            log.info("Tls配置文件不存在，跳过它");
            return;
        }

        Properties properties;
        properties = new Properties();
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(configFile);
            properties.load(inputStream);
        } catch (IOException ignore) {
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException ignore) {
                }
            }
        }

        tlsServerNeedClientAuth = properties.getProperty(TLS_SERVER_NEED_CLIENT_AUTH, tlsServerNeedClientAuth);
        tlsServerKeyPath = properties.getProperty(TLS_SERVER_KEYPATH, tlsServerKeyPath);
        tlsServerKeyPassword = properties.getProperty(TLS_SERVER_KEYPASSWORD, tlsServerKeyPassword);
        tlsServerCertPath = properties.getProperty(TLS_SERVER_CERTPATH, tlsServerCertPath);
        tlsServerAuthClient = Boolean.parseBoolean(properties.getProperty(TLS_SERVER_AUTHCLIENT, String.valueOf(tlsServerAuthClient)));
        tlsServerTrustCertPath = properties.getProperty(TLS_SERVER_TRUSTCERTPATH, tlsServerTrustCertPath);
    }

    private static void logTheFinalUsedTlsConfig() {
        log.info("Log the final used tls related configuration");
        log.info("{} = {}", TLS_SERVER_NEED_CLIENT_AUTH, tlsServerNeedClientAuth);
        log.info("{} = {}", TLS_SERVER_KEYPATH, tlsServerKeyPath);
        log.info("{} = {}", TLS_SERVER_KEYPASSWORD, tlsServerKeyPassword);
        log.info("{} = {}", TLS_SERVER_CERTPATH, tlsServerCertPath);
        log.info("{} = {}", TLS_SERVER_AUTHCLIENT, tlsServerAuthClient);
        log.info("{} = {}", TLS_SERVER_TRUSTCERTPATH, tlsServerTrustCertPath);
    }

    private static ClientAuth parseClientAuthMode(String authMode) {
        if (null == authMode || authMode.trim().isEmpty()) {
            return ClientAuth.NONE;
        }

        for (ClientAuth clientAuth : ClientAuth.values()) {
            if (clientAuth.name().equals(authMode.toUpperCase())) {
                return clientAuth;
            }
        }

        return ClientAuth.NONE;
    }

    private static boolean isNullOrEmpty(String s) {
        return s == null || s.isEmpty();
    }

}
